/*
 * Copyright (c) 2022-2022 Huawei Device Co., Ltd. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this list of
 *    conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
 *    of conditions and the following disclaimer in the documentation and/or other materials
 *    provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its contributors may be used
 *    to endorse or promote products derived from this software without specific prior written
 *    permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <sys/statfs.h>
#include <stdio.h>
#include "stdlib.h"
#include <unistd.h>
#include "los_printf.h"
#include "los_base.h"
#include "los_seq_buf.h"
#include "los_plimits.h"
#include "los_memlimit_pri.h"
#include "los_schedlimit_pri.h"
#include "los_processlimit_pri.h"
#include "los_ipclimit_pri.h"
#include "internal.h"
#include "proc_fs.h"
#include "los_task_pri.h"
#include "los_process_pri.h"
#include "los_process.h"
#include "los_typedef.h"
#include "los_list.h"
#include "show.h"
#include "vnode.h"
#include "proc_file.h"
#include "los_devicelimit_pri.h"

#define PLIMITS_ENTRY_NAME_MAX 64
#define MAX_PROTECTED_PROCESS_ID 14
#define PLIMITERSET_DELETE_ALLOC 4
#define UNITPTR_NULL ((uintptr_t)(0xFFFFFFFF))

struct ProcLimitsEntryOption {
    UINT32 id;
    char name[PLIMITS_ENTRY_NAME_MAX];
    mode_t mode;
    uintptr_t offset;
    struct ProcFileOperations ops;
};

#define PLIMIT_FILE_MODE_READ_WRITE        (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
#define PLIMIT_FILE_MODE_READ_ONLY         (S_IRUSR | S_IRGRP | S_IROTH)
#define PLIMIT_FILE_MODE_WRITE_ONLY         (S_IWUSR)
#define PLIMIT_FILE_MODE_MASK_WRITE        (~((mode_t)(S_IWUSR)))
#define PLIMIT_FILE_MODE_MASK_NONE         (~((mode_t)(0)))
#define LOS_MAX_CACHE (UINT64)(0xFFFFFFFFFFFFFFFF)
#define PLIMIT_CAT_BUF_SIZE  512
#define UNITPTR_NULL  ((uintptr_t)(0xFFFFFFFF))

extern int VfsProcfsCreate(struct Vnode* parent, const char *name, int mode, struct Vnode **vnode);
extern int CheckProcName(const char *name, struct ProcDirEntry **parent, const char **lastName);
extern struct ProcDirEntry *ProcFindNode(struct ProcDirEntry *parent, const char *name);
struct ProcDirEntry *SyncCreateProcEntry(const char *name, mode_t mode, struct ProcDirEntry *parent);
extern int DetechProcEntryData(struct ProcDirEntry *parent, const char *name, void** data);
extern void FreeProcEntry(struct ProcDirEntry *entry);

static int VfsProcfsPlimitsMkdir(struct Vnode *parent, const char *dirName, mode_t mode, struct Vnode **vnode);
static int VfsProcfsPlimitsRmdir(const char *name, struct ProcDirEntry *parent);

static ssize_t ProcLimitsAddLimiters(struct ProcFile *pf, const char *buf, size_t count, loff_t *ppos);
static ssize_t ProcLimitsReleaseLimiters(struct ProcFile *pf, const char *buf, size_t count, loff_t *ppos);
static ssize_t ProcLimitsCopyLimiters(struct ProcDirEntry *dirEntry);
static ssize_t ProcLimitsShowLimiters(struct SeqBuf *m, void *v);
static int ProcLimitsShow64(struct SeqBuf *m, void *v);
ssize_t ProcLimiterVariableWrite64(struct ProcFile *pf, const char *buf, size_t count, loff_t *ppos);
void ProcLimiterDirEntryInit(struct ProcDirEntry *dirEntry, UINT32 mask, mode_t mode);
static int ShowPids(struct SeqBuf *seqBuf, void *data);
ssize_t PidMigrateFromProcLimiterSet (struct ProcFile *pf, const char *buf, size_t count, loff_t *ppos);
ssize_t PidsMaxVariableWrite64(struct ProcFile *pf, const char *buf, size_t count, loff_t *ppos);
int ProcLimitsShowMemStat(struct SeqBuf *seqBuf, void *data);
int ProcLimitsShowSchedStat(struct SeqBuf *seqBuf, void *data);
void ProcLimiterSetReleaseFiles(struct ProcDirEntry *pde);
static int FreeProcLimiterSetFilesVnode(struct ProcDirEntry *currDirectory, const char *filename);

static struct ProcDirOperations g_procDirOperations = {
    .Mkdir = VfsProcfsPlimitsMkdir,
    .Rmdir = VfsProcfsPlimitsRmdir,
};

struct ProcLimitsEntryOption g_procLimitsEntryOpts[] = {
    {
        .id = PROCESS_LIMITER_GENERAL,
        .name = "plimits.limiters",
        .mode = PLIMIT_FILE_MODE_READ_ONLY,
        .offset = UNITPTR_NULL,
        .ops = {
            .read = ProcLimitsShowLimiters,
        }
    },
    {
        .id = PROCESS_LIMITER_GENERAL,
        .name = "plimits.limiter_add",
        .mode = PLIMIT_FILE_MODE_READ_WRITE,
        .offset = UNITPTR_NULL,
        .ops = {
            .write = ProcLimitsAddLimiters,
        },
    },
    {
        .id = PROCESS_LIMITER_GENERAL,
        .name = "plimits.limiter_delete",
        .mode = PLIMIT_FILE_MODE_READ_WRITE,
        .offset = UNITPTR_NULL,
        .ops = {
            .write = ProcLimitsReleaseLimiters,
        },
    },
    {
        .id = PROCESS_LIMITER_GENERAL,
        .name = "plimits.procs",
        .mode = PLIMIT_FILE_MODE_READ_WRITE,
        .offset = UNITPTR_NULL,
        .ops = {
            .read = ShowPids,
            .write = PidMigrateFromProcLimiterSet,
        },
    },
    {
        .id = PROCESS_LIMITER_ID_PIDS,
        .name = "pids.max",
        .mode = PLIMIT_FILE_MODE_READ_WRITE,
        .offset = LOS_OFF_SET_OF(ProcPidLimiter, max),
        .ops = {
            .read = ProcLimitsShow64,
            .write = PidsMaxVariableWrite64,
        },
    },
    {
        .id = PROCESS_LIMITER_ID_MEM,
        .name = "memory.oom_ctrl",
        .mode = PLIMIT_FILE_MODE_READ_WRITE,
        .offset = UNITPTR_NULL,
        .ops = {
           .read = OsPlimiterOomShow,
           .write = OsPlimiterOomWrite,
       },
    },

    {
        .id = PROCESS_LIMITER_ID_MEM,
        .name = "memory.failcnt",
        .mode = PLIMIT_FILE_MODE_READ_ONLY,
        .offset = LOS_OFF_SET_OF(ProcMemLimiter, failcnt),
        .ops = {
            .read = ProcLimitsShow64,
        },
    },
    {
        .id = PROCESS_LIMITER_ID_MEM,
        .name = "memory.limit",
        .mode = PLIMIT_FILE_MODE_READ_WRITE,
        .offset = LOS_OFF_SET_OF(ProcMemLimiter, limit),
        .ops = {
            .read = ProcLimitsShow64,
            .write = ProcLimiterVariableWrite64,
        },
    },
    {
        .id = PROCESS_LIMITER_ID_MEM,
        .name = "memory.peak",
        .mode = PLIMIT_FILE_MODE_READ_ONLY,
        .offset = LOS_OFF_SET_OF(ProcMemLimiter, peak),
        .ops = {
            .read = ProcLimitsShow64,
        },
    },
    {
        .id = PROCESS_LIMITER_ID_MEM,
        .name = "memory.usage",
        .mode = PLIMIT_FILE_MODE_READ_ONLY,
        .offset = LOS_OFF_SET_OF(ProcMemLimiter, usage),
        .ops = {
            .read = ProcLimitsShow64,
        },
    },
    {
        .id = PROCESS_LIMITER_ID_MEM,
        .name = "memory.stat",
        .mode = PLIMIT_FILE_MODE_READ_ONLY,
        .offset = LOS_OFF_SET_OF(ProcMemLimiter, stat),
        .ops = {
            .read = ProcLimitsShowMemStat,
        }
    },
    {
        .id = PROCESS_LIMITER_ID_SCHED,
        .name = "sched.period",
        .mode = PLIMIT_FILE_MODE_READ_WRITE,
        .offset = LOS_OFF_SET_OF(ProcSchedLimiter, period),
        .ops = {
            .read = ProcLimitsShow64,
            .write = ProcLimiterVariableWrite64,
        },
    },
    {
        .id = PROCESS_LIMITER_ID_SCHED,
        .name = "sched.quota",
        .mode = PLIMIT_FILE_MODE_READ_WRITE,
        .offset = LOS_OFF_SET_OF(ProcSchedLimiter, quota),
        .ops = {
            .read = ProcLimitsShow64,
            .write = ProcLimiterVariableWrite64,
        },
    },
    {
        .id = PROCESS_LIMITER_ID_SCHED,
        .name = "sched.stat",
        .mode = PLIMIT_FILE_MODE_READ_ONLY,
        .offset = LOS_OFF_SET_OF(ProcSchedLimiter, stat),
        .ops = {
            .read = ProcLimitsShowSchedStat,
        }
    },
    {
        .id = PROCESS_LIMITER_ID_IPC,
        .name = "ipc.mq_count",
        .mode = PLIMIT_FILE_MODE_READ_WRITE,
        .offset = LOS_OFF_SET_OF(ProcIPCLimiter, mqCountLimit),
        .ops = {
            .read = ProcLimitsShow64,
            .write = ProcLimiterVariableWrite64,
        }
    },
    {
        .id = PROCESS_LIMITER_ID_IPC,
        .name = "ipc.shm_size",
        .mode = PLIMIT_FILE_MODE_READ_WRITE,
        .offset = LOS_OFF_SET_OF(ProcIPCLimiter, shmSizeLimit),
        .ops = {
            .read = ProcLimitsShow64,
            .write = ProcLimiterVariableWrite64,
        }
    },
    {
        .id = PROCESS_LIMITER_ID_IPC,
        .name = "ipc.stat",
        .mode = PLIMIT_FILE_MODE_READ_ONLY,
        .offset = UNITPTR_NULL,
        .ops = {
            .read = IPCLimiterShowStat,
        }
    },
    {
        .id = PROCESS_LIMITER_ID_DEV,
        .name = "devices.allow",
        .mode = PLIMIT_FILE_MODE_WRITE_ONLY,
        .offset = LOS_OFF_SET_OF(ProcDevLimiter, allowFile),
        .ops = {
            .write = ProcDevLimiterWriteAllow,
        }
    },
    {
        .id = PROCESS_LIMITER_ID_DEV,
        .name = "devices.deny",
        .mode = PLIMIT_FILE_MODE_WRITE_ONLY,
        .offset = LOS_OFF_SET_OF(ProcDevLimiter, denyFile),
        .ops = {
            .write = ProcDevLimiterWriteDeny,
        }
    },
    {
        .id = PROCESS_LIMITER_ID_DEV,
        .name = "devices.list",
        .mode = PLIMIT_FILE_MODE_READ_ONLY,
        .offset = UNITPTR_NULL,
        .ops = {
            .read = ProcDevLimiterShow,
        }
    },
};

static UINT32 PlimiterCount(struct ProcDirEntry *pn, const char *name)
{
    UINT32 subdircount = 0;
    struct ProcDirEntry *iter = ProcFindNode(pn, name);
    if (iter == NULL) {
        return 0;
    }
    iter = iter->subdir;
    while (iter != NULL) {
        subdircount++;
        iter = iter->next;
    }
    return subdircount;
}

static int ProcLimitsRelease(const char *name, struct ProcDirEntry *parent)
{
    void **data = NULL;
    ProcLimiterSet *procLimiterSet = NULL;
    const char *lastName = name;

    if (CheckProcName(name, &parent, &lastName) != 0) {
        return LOS_NOK;
    }
    if (PlimiterCount(parent,lastName) > PLIMITERSET_DELETE_ALLOC) {
        return LOS_NOK;
    }
    if (DetechProcEntryData(parent, lastName, data) != LOS_OK) {
        return LOS_NOK;
    }
    procLimiterSet = (ProcLimiterSet *)(*data);
    if (procLimiterSet != NULL) {
        ProcLimiterSetFree(procLimiterSet);
    }
    return LOS_OK;
}

int VfsProcfsPlimitsMkdir(struct Vnode *parent, const char *dirName, mode_t mode, struct Vnode **vnode)
{
    int ret = LOS_NOK;
    ret = VfsProcfsCreate(parent, dirName, S_IFDIR | mode, vnode);
    if (ret != LOS_OK) {
        return ret;
    }
    struct ProcDirEntry *currEntry = (struct ProcDirEntry *)((*vnode)->data);
    currEntry->procDirOps = &g_procDirOperations;
    ret = ProcLimitsCopyLimiters(currEntry);
    if (ret != LOS_OK) {
        VnodeFree(*vnode);
        ProcFreeEntry(currEntry);
        return -ENOSYS;
    }
    return ret;
}

int VfsProcfsPlimitsRmdir(const char *name, struct ProcDirEntry *parent)
{
    LOS_ASSERT(parent != NULL);
    if (ProcLimitsRelease(name, parent) != LOS_OK) {
        return -LOS_NOK;
    }
    if (RemoveProcEntry(name, parent) != LOS_OK) {
        return -LOS_NOK;
    }
    return LOS_OK;
}

static inline struct ProcDirEntry *GetCurrDirectory(struct ProcDirEntry *dirEntry)
{
    return ((dirEntry == NULL) || S_ISDIR(dirEntry->mode)) ? dirEntry : dirEntry->parent;
}

static inline ProcLimiterSet *GetProcLimiterSetFromDirEntry(struct ProcDirEntry *dirEntry)
{
    struct ProcDirEntry *currDirectory = GetCurrDirectory(dirEntry);
    return (currDirectory == NULL) || (currDirectory->data == NULL) ? NULL : (ProcLimiterSet *)currDirectory->data;
}

ssize_t ProcLimitsShowLimiters(struct SeqBuf *seqBuf, void *data)
{
    ProcLimiterSet *procLimiterSet = (ProcLimiterSet *)data;

    if (procLimiterSet == NULL) {
        PRINT_ERR("plimits.limiters is NULL.\n");
        return -LOS_NOK;
    }
    if (procLimiterSet->mask & BIT(PROCESS_LIMITER_ID_PIDS)) {
        LosBufPrintf(seqBuf, "%s", "pids\n");
    }
    if (procLimiterSet->mask & BIT(PROCESS_LIMITER_ID_MEM)) {
        LosBufPrintf(seqBuf, "%s", "memory\n");
    }
    if (procLimiterSet->mask & BIT(PROCESS_LIMITER_ID_SCHED)) {
        LosBufPrintf(seqBuf, "%s", "sched\n");
    }
    if (procLimiterSet->mask & BIT(PROCESS_LIMITER_ID_IPC)) {
        LosBufPrintf(seqBuf, "%s", "ipc\n");
    }
    if (procLimiterSet->mask & BIT(PROCESS_LIMITER_ID_DEV)) {
        LosBufPrintf(seqBuf, "%s", "devices\n");
    }
    return LOS_OK;
}

ssize_t ProcLimiterVariableWrite64(struct ProcFile *pf, const char *buf, size_t count, loff_t *ppos)
{
    CHAR *endPtr = NULL;
    UINT64 limit;
    ProcLimiterSet *procLimiterSet = GetCurrDirectory(pf->pPDE)->data;
    LOS_ASSERT(procLimiterSet != NULL);
    if (!ProcLimiterSetAllowWrite(procLimiterSet)) {
        PRINT_ERR("\nroot plimits files are not permitted to add.\n");
        return -LOS_NOK;
    }

    limit = strtoull(buf, &endPtr, 0);
    if ((endPtr == NULL) || (*endPtr != 0)) {
        PRINT_ERR("input value '%s' is not invalid.\n", buf);
        return -LOS_NOK;
    }

    if (limit >= LOS_MAX_CACHE) {
        PRINT_ERR("input value '%s' cannot be greater than %llu\n",buf, LOS_MAX_CACHE);
        return -LOS_NOK;
    }

    UINT64 *value = (UINT64 *)pf->pPDE->data;
    if (value == NULL) {
        PRINT_ERR("%s() %d line, DirEntry point is Null, please check the kernel code.\n", __FUNCTION__, __LINE__);
        return -LOS_NOK;
    }
    *value = limit;
    return count;
}

static void FilePath(char *dirName, char *filepath)
{
    int dirLen = strlen(dirName) + 3;
    char temp[dirLen];
    memset_s(temp, sizeof(temp), 0, sizeof(temp));
    char path[NAME_MAX];
    memset_s(path, sizeof(path), 0, sizeof(path));
    int ret = sprintf_s(temp, sizeof(temp), "/%s", dirName);
    if (ret < 0) {
        PRINT_ERR("FilePath create error.\n");
        return;
    }
    strcpy_s(path, sizeof(path), temp);
    strncat_s(path, sizeof(path), filepath, strlen(filepath));
    memset_s(filepath, NAME_MAX, 0, NAME_MAX);
    strcpy_s(filepath, NAME_MAX, path);
    return;
}

static int FreeProcLimiterSetFilesVnode(struct ProcDirEntry *currDirectory, const char *filename)
{
    char filepath[NAME_MAX] = {'\0'};
    char *fullpath = NULL;
    int ret;
    struct Vnode *vnode = NULL;
    struct ProcDirEntry **iter = NULL;
    char *prefix = GetProcRootEntry()->name;
    char temp[strlen(filename) + 2];
    memset_s(temp, sizeof(temp), 0, sizeof(temp));
    char *working_directory = OsShellGetWorkingDirectory();

    iter = &currDirectory;
    while (strncmp((*iter)->name, prefix, strlen(prefix)) != 0) {
        FilePath((*iter)->name, filepath);
        iter = &(*iter)->parent;
    }
    FilePath("proc", filepath);
    sprintf_s(temp, sizeof(temp), "/%s", filename);
    strncat_s(filepath, sizeof(filepath), temp, strlen(temp));
    ret = vfs_normalize_path(working_directory, filepath, &fullpath);
    if (ret < 0) {
        set_errno(-ret);
        return -LOS_NOK; /* build path failed */
    }

    VnodeHold();
    ret = VnodeLookup(fullpath, &vnode, O_RDONLY);
    if (ret != LOS_OK) {
        set_errno(-ret);
        VnodeDrop();
        free(fullpath);
        return -LOS_NOK;
    }
    VnodeDrop();
    VnodeFree(vnode);
    free(fullpath);
    return LOS_OK;
}

void ProcLimitsInit(void)
{
    struct ProcDirEntry *parentPDE = NULL;

    ProcLimiterSetRootInit();

    parentPDE = CreateProcEntry("plimits", S_IFDIR | S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH, NULL);
    if (parentPDE == NULL) {
        PRINT_ERR("create /proc/plimits error.\n");
        return;
    }
    parentPDE->procDirOps = &g_procDirOperations;
    parentPDE->data = (void *)&g_procLimiterSet;
    UINT32 mask = 0;
    mask |= (BIT(PROCESS_LIMITER_GENERAL) | BIT(PROCESS_LIMITER_ID_PIDS) |
            BIT(PROCESS_LIMITER_ID_MEM) | BIT(PROCESS_LIMITER_ID_SCHED) |
            BIT(PROCESS_LIMITER_ID_IPC) | BIT(PROCESS_LIMITER_ID_DEV));
    ProcLimiterDirEntryInit(parentPDE, mask, PLIMIT_FILE_MODE_MASK_WRITE);
    g_isInitProcLimiterSet = TRUE;
    UpdateMigrateStatus(true);
    return;
}

int ProcLimitsShow64(struct SeqBuf *seqBuf, void *data)
{
    UINT64 *value = (UINT64 *)data;
    UINT32 ret;
    if (seqBuf == NULL) {
        PRINT_ERR("seqBuf is NULL.\n");
        return -LOS_NOK;
    }
    if (value == NULL) {
        PRINT_ERR("this data is NULL.\n");
        return -LOS_NOK;
    }
    ret = LosBufPrintf(seqBuf, "%lu\n", (INT64)*value);

    if ((ret != 0) || (seqBuf->buf == NULL)) {
        PRINTK("cache operation error.\n");
        return -LOS_NOK;
    }
    return LOS_OK;
}

void ProcLimiterSetRemoveFiles(struct ProcDirEntry *pde, char *prefix)
{
    struct ProcDirEntry *currDirectory = NULL;
    struct ProcDirEntry **iter = NULL;

    currDirectory = GetCurrDirectory(pde);
    if (currDirectory == NULL) {
        PRINT_ERR("%s() %d line, the file directory is NULL.\n", __FUNCTION__, __LINE__);
        return;
    }
    iter = &currDirectory->subdir;

    while (*iter != NULL) {
        if (!strncmp((*iter)->name, prefix, strlen(prefix)) && ((((*iter)->mode) & S_IFMT) != S_IFDIR)) {
            (*iter)->mode |= FMODE_DIRDEL;
            FreeProcLimiterSetFilesVnode(currDirectory, (*iter)->name);
            struct ProcDirEntry *pn = *iter;
            *iter = pn->next;
            pn->parent = NULL;
            (void)FreeProcEntry(pn);
            continue;
        }
        iter = &(*iter)->next;
    }
    return;
}

void ProcLimiterSetReleaseFiles(struct ProcDirEntry *pde)
{
    struct ProcDirEntry *currDirectory = NULL;
    struct ProcDirEntry **iter = NULL;

    currDirectory = GetCurrDirectory(pde);
    currDirectory->mode |= FMODE_DIRDEL;
    iter = &currDirectory->subdir;

    while (*iter != NULL) {
        (*iter)->mode |= FMODE_DIRDEL;
        iter = &(*iter)->next;
    }
    pde->mode |= FMODE_DIRDEL;
}

static void ProcLimitsCreateLimiters(ProcLimiterSet *procLimiterSet, UINT32 *mask)
{
    for (enum ProcLimiterId plimiterId = PROCESS_LIMITER_GENERAL; plimiterId < PROCESS_LIMITER_COUNT; ++plimiterId) {
        if (!(*mask & BIT(plimiterId))) {
            continue;
        }
        int ret = ProcLimiterSetAddLimiter(procLimiterSet, plimiterId);
        if (ret != LOS_OK) {
            *mask = *mask & (~BIT(plimiterId));
        }
    }
    return;
}

ssize_t ProcLimitsAddLimiters(struct ProcFile *pf, const char *buf, size_t count, loff_t *ppos)
{
    struct ProcDirEntry *dirEntry = pf->pPDE;
    ProcLimiterSet *procLimiterSet = GetProcLimiterSetFromDirEntry(dirEntry);
    if (procLimiterSet->level == 0) {
        PRINT_ERR("\nroot plimits files are not permitted to add.\n");
        return -LOS_NOK;
    }

    UINT32 mask = 0;
    if (strcmp(buf, "pids") == 0) {
        mask |= BIT(PROCESS_LIMITER_ID_PIDS);
    } else if (strcmp(buf, "memory") == 0) {
        mask |= BIT(PROCESS_LIMITER_ID_MEM);
    } else if (strcmp(buf, "sched") == 0) {
        mask |= BIT(PROCESS_LIMITER_ID_SCHED);
    } else if (strcmp(buf, "ipc") == 0) {
        mask |= BIT(PROCESS_LIMITER_ID_IPC);
    } else if (strcmp(buf, "devices") == 0) {
        mask |= BIT(PROCESS_LIMITER_ID_DEV);
    } else {
        PRINTK("enter error, please check the params.\n");
        return -LOS_NOK;
    }
    ProcLimitsCreateLimiters(procLimiterSet, &mask);
    ProcLimiterDirEntryInit(dirEntry, mask, PLIMIT_FILE_MODE_MASK_NONE);
    return count;
}

static void ProcLimitsMigrateToRoot(ProcLimiterSet *procLimiterSet)
{
    UINT32 intSave;
    LosProcessCB *pcb = NULL;
    UINT32 pid = 1;
    unsigned int pidMaxNum = LOS_GetSystemProcessMaximum();
    SCHEDULER_LOCK(intSave);
    for (pid  = 1; pid < pidMaxNum; ++pid) {
        pcb = OS_PCB_FROM_PID(pid);
        if (OsProcessIsDead(pcb)) {
            continue;
        }
        if (g_procLimiterSetArray[pcb->processID] == procLimiterSet) {
            g_procLimiterSetArray[pcb->processID] = &g_procLimiterSet;
        }
    }
    SCHEDULER_UNLOCK(intSave);
    return;
}

ssize_t ProcLimitsReleaseLimiters(struct ProcFile *pf, const char *buf, size_t count, loff_t *ppos)
{
    struct ProcDirEntry *pde = NULL;
    if ((pf == NULL) || (pf->pPDE == NULL)) {
        PRINT_ERR("%s() %d line, procfile or procfie->pde is NULL.\n", __FUNCTION__, __LINE__);
        return -LOS_NOK;
    }

    pde = pf->pPDE;
    ProcLimiterSet *procLimiterSet = GetProcLimiterSetFromDirEntry(pde);
    if (procLimiterSet == NULL) {
        PRINT_ERR("%s() %d line, procLimiterSet is NULL.\n", __FUNCTION__, __LINE__);
        return -LOS_NOK;
    }
    if (procLimiterSet->level == 0) {
        PRINT_ERR("\nroot plimits files are not permitted to delete.\n");
        return -LOS_NOK;
    }
    enum ProcLimiterId procLimiterId = 0;
    if (strcmp(buf, "pids") == 0) {
        procLimiterId = PROCESS_LIMITER_ID_PIDS;
        ProcLimiterSetRemoveFiles(pde, "pids");
        ProcLimitsMigrateToRoot(procLimiterSet);
    } else if (strcmp(buf, "memory") == 0) {
        procLimiterId = PROCESS_LIMITER_ID_MEM;
        ProcLimiterSetRemoveFiles(pde, "memory");
    } else if (strcmp(buf, "sched") == 0) {
        procLimiterId = PROCESS_LIMITER_ID_SCHED;
        ProcLimiterSetRemoveFiles(pde, "sched");
    } else if (strcmp(buf, "ipc") == 0) {
        procLimiterId = PROCESS_LIMITER_ID_IPC;
        ProcLimiterSetRemoveFiles(pde, "ipc");
    } else if (strcmp(buf, "devices") == 0) {
        procLimiterId = PROCESS_LIMITER_ID_DEV;
        DevLimiterFree(pde);       /* Free devLimit list space */
        ProcLimiterSetRemoveFiles(pde, "devices");
    } else {
        PRINTK("the input information or format is incorrect.\n");
        return -LOS_NOK;
    }
    ProcLimiterSetReleaseLimiter(procLimiterSet, procLimiterId);
    procLimiterSet->mask &= (~BIT(procLimiterId));
    UpdateMigrateStatus(true);
    if (procLimiterSet->mask == BIT(PROCESS_LIMITER_GENERAL)) {
        ProcLimiterSetReleaseFiles(pde);
    }
    return count;
}

void ProcLimiterDirEntryInit(struct ProcDirEntry *dirEntry, UINT32 mask, mode_t mode)
{
    struct ProcDirEntry *pde = NULL;
    ProcLimiterSet *procLimiterSet = NULL;
    void *header = NULL;
    struct ProcLimitsEntryOption *opt = NULL;
    UINT8 plimiterId = 0;

    struct ProcDirEntry *currDir = GetCurrDirectory(dirEntry);
    if (currDir == NULL) {
        PRINT_ERR("%s %d, current directory is NULL.\n", __FUNCTION__, __LINE__);
        return;
    }
    procLimiterSet = (ProcLimiterSet *)currDir->data;
    if (procLimiterSet == NULL) {
        PRINT_ERR("%s %d, pde->data is NULL.\n", __FUNCTION__, __LINE__);
        return;
    }
    for (int i = 0; i < sizeof(g_procLimitsEntryOpts) / sizeof(struct ProcLimitsEntryOption); ++i) {
        opt = &g_procLimitsEntryOpts[i];
        plimiterId = opt->id;
        if (!(BIT(plimiterId) & mask)) {
            continue;
        }

        pde = SyncCreateProcEntry(opt->name, opt->mode & mode, currDir);
        if (pde == NULL) {
            PRINT_ERR("creat /proc/plimits/memory.* error.\n");
            return;
        }
        header = (opt->offset == UNITPTR_NULL) ? procLimiterSet : (void *)procLimiterSet->procLimiterList[plimiterId]->private;
        pde->data = (void *)((char *)header + ((opt->offset != UNITPTR_NULL) ? opt->offset : 0 ));
        pde->procFileOps = &opt->ops;
        pde->procDirOps = &g_procDirOperations;
    }
    currDir->mode &= ~FMODE_DIRDEL;
    procLimiterSet->mask |= mask;
    return;
}

ssize_t ProcLimitsCopyLimiters(struct ProcDirEntry *dirEntry)
{
    struct ProcDirEntry *parentPde;
    ProcLimiterSet *parentPlimiterSet = NULL;
    if ((dirEntry == NULL) || (dirEntry->parent == NULL)) {
        PRINT_ERR("%s() %d line, dirEntry is NULL.\n", __FUNCTION__, __LINE__);
        return -LOS_NOK;
    }
    parentPde = dirEntry->parent;
    parentPlimiterSet = (ProcLimiterSet *)parentPde->data;
    if (parentPlimiterSet == NULL) {
        PRINT_ERR("%s() %d line, parent plimits is NULL.\n", __FUNCTION__, __LINE__);
        return -LOS_NOK;
    }
    ProcLimiterSet *plimiterSet = ProcLimiterSetCreateFromParent(parentPlimiterSet);
    if (plimiterSet == NULL) {
        PRINT_ERR("plimiter set alloc failed.\n");
        return -LOS_NOK;
    }
    dirEntry->data = (void *)plimiterSet;
    ProcLimiterDirEntryInit(dirEntry, parentPlimiterSet->mask, PLIMIT_FILE_MODE_MASK_NONE);
    return LOS_OK;
}

#define PLIMITS_PID_STR_LENGTH 4
int ShowPids(struct SeqBuf *seqBuf, void *data)
{
    UINT32 intSave;
    UINT32 pid = 1;
    char buf[PLIMIT_CAT_BUF_SIZE];
    char pidStr[PLIMITS_PID_STR_LENGTH] = {'\0'};
    if (data == NULL) {
        PRINT_ERR("procLimiterSet is NULL.\n");
        return -LOS_NOK;
    }

    (void)memset(buf, 0, sizeof(buf));
    LosProcessCB *pcb = NULL;
    UINT32 pidMaxNum = LOS_GetSystemProcessMaximum();
    ProcLimiterSet *procLimiterSet = (ProcLimiterSet *)data;
    (void)LosBufPrintf(seqBuf, "\n");
    SCHEDULER_LOCK(intSave);
    for (pid  = 1; pid < pidMaxNum; ++pid) {
        pcb = OS_PCB_FROM_PID(pid);
        if (OsProcessIsDead(pcb)) {
            continue;
        }
        if (g_procLimiterSetArray[pcb->processID] == procLimiterSet) {
            (void)sprintf_s(pidStr, PLIMITS_PID_STR_LENGTH, "%d\n", pcb->processID);
            (void)strcat_s(buf, sizeof(buf), pidStr);
        }
    }
    SCHEDULER_UNLOCK(intSave);
    (void)LosBufPrintf(seqBuf, "%s", buf);
    return LOS_OK;
}

ssize_t PidMigrateFromProcLimiterSet(struct ProcFile *pf, const char *buf, size_t count, loff_t *ppos)
{
    UINT32 pid = OS_INVALID_VALUE;
    LosProcessCB *processCB = NULL;
    ProcLimiterSet *procLimiterSet = NULL;
    procLimiterSet = GetCurrDirectory(pf->pPDE)->data;
    if (!ProcLimiterSetAllowWrite(procLimiterSet)) {
        PRINT_ERR("\nroot plimits files are not permitted to add.\n");
        return -LOS_NOK;
    }
    if (buf == NULL || pf == NULL) {
        return -LOS_NOK;
    }
    if (ProcLimiterSetGetPidCount(procLimiterSet) >= ProcLimiterSetGetPidMax(procLimiterSet)) {
        PRINT_ERR("\npid is not permitted to add.\n");
        return -LOS_NOK;
    }
    pid = (UINT32)atoi(buf);
    if (pid <= MAX_PROTECTED_PROCESS_ID) {
        PRINT_ERR("\nprotected pid is not permitted to add.\n");
        return -LOS_NOK;
    }
    if (OsProcessIDUserCheckInvalid(pid)) {
        PRINT_ERR("write invalid pid = %u", pid);
        return -LOS_NOK;
    }
    processCB = OS_PCB_FROM_PID(pid);
    if (OsProcessIsUnused(processCB)) {
        PRINT_ERR("write error pid = %u", pid);
        return -LOS_NOK;
    }
    g_procLimiterSetArray[processCB->processID] = procLimiterSet;
    UpdateMigrateStatus(true);
    return count;
}

ssize_t PidsMaxVariableWrite64(struct ProcFile *pf, const char *buf, size_t count, loff_t *ppos)
{
    (void)ppos;
    UINT64 pidMax = 0;
    UINT32 pidCount = 0;
    CHAR *endPtr = NULL;
    UINT32 systemPidnum = LOS_GetSystemProcessMaximum();

    ProcLimiterSet *procLimiterSet = GetCurrDirectory(pf->pPDE)->data;
    LOS_ASSERT(procLimiterSet != NULL);
    if (!ProcLimiterSetAllowWrite(procLimiterSet)) {
        PRINT_ERR("\nroot plimits files are not permitted to add.\n");
        return -LOS_NOK;
    }

    pidMax = strtoull(buf, &endPtr, 0);
    if ((endPtr == NULL) || (*endPtr != 0)) {
        PRINT_ERR("input value '%s' is not a invalid pid.\n", buf);
        return -LOS_NOK;
    }

    if (pidMax > systemPidnum) {
        PRINT_ERR("input value '%s' is not a valid value.\n", buf);
        return -LOS_NOK;
    }

    UINT64 *value = (UINT64 *)pf->pPDE->data;
    if (value == NULL) {
        PRINT_ERR("%s() %d line, DirEntry point is Null, please check the kernel code.\n", __FUNCTION__, __LINE__);
        return -LOS_NOK;
    }
    pidCount = ProcLimiterSetGetPidCount(procLimiterSet);
    if (pidMax <= pidCount) {
        PRINT_ERR("pids.max [%s] must be greater than [%d].\n", buf, pidCount);
        return -LOS_NOK;
    }
    *value = pidMax;
    return count;
}

int ProcLimitsShowMemStat(struct SeqBuf *seqBuf, void *data)
{
    return OsMemLimiterShowStat(seqBuf, data);
}

int ProcLimitsShowSchedStat(struct SeqBuf *seqBuf, void *data)
{
    SchedLimiterShowStat(seqBuf, data);
    return LOS_OK;
}

